﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net;

namespace BC2CitadelDaemon
{
    public struct pWords
    {
        public int size;
        public byte[] words;
        

        public pWords(int aSize, byte[] aWords)
        {
            size = aSize;
            words = aWords;
        }
    }

    public class Packet
    {
        
        private string[] words;




        public int Sequence
        { get; set; }

        public bool IsFromServer
        { get; set; }


        public bool IsResponse
        { get; set; }

        public List<String> Words
        {
            set { words = value.ToArray(); }
            get { return words.ToList(); }
        }

        public string CommandString
        {
            set
            {
                // break up the command string into individual words.
                // This will be useful when we are allowing command line input
                // from the remote client app.
                words = value.Split(' ');
            }
        }

        public Packet()
        {
        }

        public Packet(byte[] bytes)
        {
        }

        public byte[] EncodeHeader()
        {
            // Will need to add a byte order check here for the host and make sure
            // we are outputing everything as little endian for the stupid servers
            // incase we are running the server on a bigendian machine, however
            // unlikely.

            uint header = 0;

            if (BitConverter.IsLittleEndian)
            {
                header = (uint)Sequence & 0x3fffffff;

                if (IsFromServer)
                {
                    header += 0x80000000;
                }

                if (IsResponse)
                {
                    header += 0x40000000;
                }
            }

            return BitConverter.GetBytes(header);
        }

        static public byte[] EncodeInt32(int size)
        {
            return BitConverter.GetBytes(size);
        }

        private pWords EncodeWords()
        {
            byte[] wordarray = new byte[0];
            int size = 0;
            

            foreach (string word in words)
            {
                byte[] eLength = EncodeInt32(word.Length);
                byte[] eWord = Encoding.ASCII.GetBytes(word);
                byte[] eTerm = new byte[] { 0x00 };
                

                byte[] append = new byte[eLength.Length + eWord.Length + eTerm.Length];
                System.Buffer.BlockCopy(eLength, 0, append, 0, eLength.Length);
                System.Buffer.BlockCopy(eWord, 0, append, eLength.Length, eWord.Length);
                System.Buffer.BlockCopy(eTerm, 0, append, eLength.Length + eWord.Length, eTerm.Length);

                byte[] combine = new byte[wordarray.Length + append.Length];
                System.Buffer.BlockCopy(wordarray, 0, combine, 0, wordarray.Length);
                System.Buffer.BlockCopy(append, 0, combine, wordarray.Length, append.Length);

                wordarray = combine;

                size += word.Length + 5;
            }

            return new pWords(size, wordarray);
        }

        public void Decode(byte[] message)
        {
            // Decode the header
            byte[] messageHeader = new byte[4];

            for (int i = 0; i < 4; i++)
            {
                messageHeader[i] = message[i];
            }

            uint decodedHeader = BitConverter.ToUInt32(messageHeader, 0);
            BitArray darray = new BitArray(messageHeader);
            IsFromServer = darray[31];
            IsResponse = darray[30];
            Sequence = (int)decodedHeader & 0x3fffffff;

            // Decode the Words
            List<byte> wordCount = message.Skip(8).Take(4).ToList();

            int numWords = BitConverter.ToInt32(wordCount.ToArray(), 0);
            int bytePosition = 12;

            // Process through the words
            List<string> wordresult = new List<string>();
            for(int i = 0; i < numWords; i++)
            {
                wordresult.Add(DecodeWord(message, ref bytePosition));
            }

            words = wordresult.ToArray();
        }

        private string DecodeWord(byte[] message, ref int bytePosition)
        {
            List<byte> wordSize = message.Skip(bytePosition).Take(4).ToList();
            int numBytes = BitConverter.ToInt32(wordSize.ToArray(), 0);

            System.Text.Encoding enc = System.Text.Encoding.UTF8;

            string result = enc.GetString(message.Skip(bytePosition + 4).Take(numBytes).ToArray());

            bytePosition += 4 + numBytes + 1;
            return result;
        }


        public byte[] Encode()
        {
            // Get our byte word list and word size.
            pWords outwords = EncodeWords();

            // Get our various byte arrays in order
            byte[] eHeader = EncodeHeader();
            byte[] eNumWords = EncodeInt32(words.Length);
            byte[] eSize = EncodeInt32(outwords.size + 12);
            byte[] eCommand = outwords.words;
            
            // Create byte array to return for transmission.
            byte[] result = new byte[eHeader.Length + eNumWords.Length + eSize.Length + eCommand.Length];

            int index = 0; // Just so we can keep track of our offset, this can be done better.
            System.Buffer.BlockCopy(eHeader, 0, result, index, eHeader.Length);
            index += eHeader.Length;
            System.Buffer.BlockCopy(eSize, 0, result, index,eSize.Length);
            index += eSize.Length;
            System.Buffer.BlockCopy(eNumWords, 0, result, index, eNumWords.Length);
            index += eNumWords.Length;
            System.Buffer.BlockCopy(eCommand, 0, result, index, eCommand.Length);

            return result;
        }
    }




}
